// CRotatingBillboardNode.h file
// gaz@bitplane.net - no rights reserved

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;


inline vector3df getTargetAngle(vector3df v, vector3df r) 
{ 
	//v -position 
	//r -target 

	vector3df angle; 
	float x,y,z; 
	x = r.X - v.X; 
	y = r.Y - v.Y; 
	z = r.Z - v.Z; 
	
	//angle in X-Z plane 
	angle.Y = atan2 (x, z); 
	angle.Y *= 57.2957795; //converting from rad to degrees 

	//just making sure angle is somewhere between 0-360 degrees 
	if(angle.Y < 0) angle.Y += 360; 
	if(angle.Y >= 360) angle.Y -= 360; 
		
	//angle in Y-Z plane while Z and X axes are already rotated around Y 
	float z1 = sqrt(x*x + z*z); 
		
	angle.X = atan2 (z1, y); 
	angle.X *= 57.2957795; //converting from rad to degrees 


	//just making sure angle is somewhere between 0-360 degrees 
	if(angle.X < 0) angle.X += 360; 
	if(angle.X >= 360) angle.X -= 360; 
	
	return angle; 
} 





class TRotatingBillboardSceneNode : public ISceneNode
 {
private:

	core::dimension2d<f32> Size;
	core::aabbox3d<f32> BBox;
	video::SMaterial Material;

	s16 rotatecount, framecount, currentframe;
	s16 lastimagex;
	core::dimension2d<s32> imagesize;
	core::dimension2d<s32> texturesize;

	video::S3DVertex vertices[4];
	u16 indices[6];
	
	core::vector3df pos;
	
	core::vector3df campos;
	core::vector3df target;
	core::vector3df up;
	core::vector3df view;
	core::vector3df horizontal;
	core::vector3df vertical;
	
public:

	// constructor
	TRotatingBillboardSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id, 
	const core::vector3df& position = core::vector3df(0,0,0), 
	const core::dimension2d<f32>& size = dimension2d<f32>(10,10)) : ISceneNode(parent, mgr, id)

	{  

		setSize(size);
	
		AutomaticCullingEnabled = false;
	
		indices[0] = 0;
		indices[1] = 2;
		indices[2] = 1;
		indices[3] = 0;
		indices[4] = 3;
		indices[5] = 2;
	
		vertices[0].TCoords.set(0.0f, 0.0f);
		vertices[0].Color = 0xffffffff;
	
		vertices[1].TCoords.set(0.0f, 1.0f);
		vertices[1].Color = 0xffffffff;
	
		vertices[2].TCoords.set(1.0f, 1.0f);
		vertices[2].Color = 0xffffffff;
	
		vertices[3].TCoords.set(1.0f, 0.0f);
		vertices[3].Color = 0xffffffff;
			
	}

	~TRotatingBillboardSceneNode()
	{
	}
	
	void TRotatingBillboardSceneNode::OnRegisterSceneNode()
	{
		if (IsVisible)
		{
			SceneManager->registerNodeForRendering(this);
			ISceneNode::OnRegisterSceneNode();
		}
	}
	 
	void setSprites(s16 rotationframes, s16 animframes, core::dimension2d<s32> spritedimensions,  s16 currentanimframe =0)
	{
		rotatecount = rotationframes;
		framecount  = animframes;
		imagesize  = spritedimensions;
		currentframe = currentanimframe;
		SMaterial t = getMaterial(0);
		ITexture *tx = t.Textures[0];
		texturesize = tx->getSize();
	}
	void setFrame(s16 framenumber) 
	{
		currentframe = framenumber;
	}

	//! render
	void TRotatingBillboardSceneNode::render()
	{
		video::IVideoDriver* driver = SceneManager->getVideoDriver();
		ICameraSceneNode* camera = SceneManager->getActiveCamera();
	
		if (!camera || !driver)
			return;
	
		// make billboard look to Camera
	
		pos = getAbsolutePosition();
	
		campos = camera->getAbsolutePosition();
		target = camera->getTarget();
		up = camera->getUpVector();
		view = target - campos;
		view.normalize();
	
		horizontal = up.crossProduct(view);
		horizontal.normalize();
	
		vertical = horizontal.crossProduct(view);
		vertical.normalize();
	
		horizontal *= 0.5f * Size.Width;
		vertical *= 0.5f * Size.Height;	
	
		vertices[0].Pos = pos + horizontal + vertical;
		vertices[1].Pos = pos + horizontal - vertical;
		vertices[2].Pos = pos - horizontal - vertical;
		vertices[3].Pos = pos - horizontal + vertical;

		view *= -1.0f;
	
		for (s32 i=0; i<4; ++i)
			vertices[i].Normal = view;

		// get rotation sprite number
		vector3df selfrot=getRotation();
		vector3df targetrot;
		targetrot=getTargetAngle(pos,campos);
		f32 rotadd=targetrot.Y-selfrot.Y;
		
		s16 imagex = rotatecount-1 - int(rotadd / (360.0/rotatecount));
		
		if (lastimagex != imagex) {
			// get the texture position
			f32 x1	 = (imagex * imagesize.Width) ;
				x1	/= texturesize.Width;
			f32 y1	 = (currentframe * imagesize.Height);
				y1	/= texturesize.Height;
			f32 x2	 = ((imagex+1) * imagesize.Width); 
				x2	/= texturesize.Width;
			f32 y2	 = ((currentframe+1) * imagesize.Height);
				y2	/= texturesize.Height;
			
			// set texture
			
			vertices[0].TCoords.set(x1, y2);
			vertices[1].TCoords.set(x1, y1);
			vertices[2].TCoords.set(x2, y1);
			vertices[3].TCoords.set(x2, y2);
		}
		lastimagex = imagex;
		// draw
	
		core::matrix4 mat;
		driver->setTransform(video::ETS_WORLD, mat);
		driver->setMaterial(Material);
	
		driver->drawIndexedTriangleList(vertices, 4, indices, 2);
	}
	
	
	//! returns the axis aligned bounding box of this node
	const core::aabbox3d<f32>& TRotatingBillboardSceneNode::getBoundingBox() const
	{
		return BBox;
	}
	
	
	//! sets the size of the billboard
	void TRotatingBillboardSceneNode::setSize(const core::dimension2d<f32>& size)
	{
		Size = size;
	}
	
	
	video::SMaterial& TRotatingBillboardSceneNode::getMaterial(u32 i)
	{
		return Material;
	}
	
	
	//! returns amount of materials used by this scene node.
	u32 TRotatingBillboardSceneNode::getMaterialCount()
	{
		return 1;
	}
	
	
	//! gets the size of the billboard
	const core::dimension2d<f32>& TRotatingBillboardSceneNode::getSize()
	{
		return Size;
	}
		  
 };  
